/******************************************************************
 * Copyright (c) 2024 Bocloud Technologies Co., Ltd.
 * installer is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain n copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 ******************************************************************/

package cmd

import (
	"errors"
	"fmt"
	"net"
	"os"
	"strings"

	"github.com/spf13/cobra"
	configinit "gopkg.openfuyao.cn/bkecommon/cluster/initialize"

	"gopkg.openfuyao.cn/bkeadm/pkg/cluster"
	"gopkg.openfuyao.cn/bkeadm/pkg/initialize"
	"gopkg.openfuyao.cn/bkeadm/utils"
)

var initOption initialize.Options
var confirm bool

// initCmd represents the init command
var initCmd = &cobra.Command{
	Use:   "init",
	Short: "Initialize the boot node",
	Long:  `Initialize the boot node, including node check, warehouse start, cluster installation, and so on`,
	Example: `
# Initialize the boot node
bke init 
# Initialize with console installation enabled
bke init --installConsole=true
# Initialize with console installation disabled
bke init --installConsole=false
# Initialize the boot node and deploy the management cluster
bke init --file bkecluster.yaml
# Download files from the specified image and install the cluster
bke init --otherRepo cr.openfuyao.cn/openfuyao/bke-online-installed:latest
# Use external mirror repositories and source repositories
bke init --otherRepo cr.openfuyao.cn/openfuyao --otherSource http://192.168.1.120:40080

bke init --otherRepo cr.openfuyao.cn/openfuyao --otherSource http://192.168.100.48:8080（端口号） 

`,
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if initOption.HostIP == "" {
			ip, _ := utils.GetOutBoundIP()
			initOption.HostIP = ip
		}
		if initOption.HostIP == "" {
			ip, _ := utils.GetIntranetIp()
			initOption.HostIP = ip
		}

		// 仅当用户未传入 --domain 时，才使用默认值
		if initOption.Domain == "" {
			initOption.Domain = configinit.DefaultImageRepo
		}
		// 同理，ImageRepoPort 也需要这样处理（如果需要）
		if initOption.ImageRepoPort == "" {
			initOption.ImageRepoPort = configinit.DefaultImageRepoPort
		}

		if initOption.File != "" {
			bkeCluster, err := cluster.NewBKEClusterFromFile(initOption.File)
			if err != nil {
				return err
			}
			imageRepo := bkeCluster.Spec.ClusterConfig.Cluster.ImageRepo
			imgPort := ":443"
			if len(imageRepo.Port) > 0 {
				imgPort = ":" + imageRepo.Port
			}
			if len(imageRepo.Ip) > 0 {
				if imageRepo.Ip != initOption.HostIP {
					if imageRepo.Domain == "" || imageRepo.Domain == configinit.DefaultImageRepo {
						initOption.OtherRepo = imageRepo.Ip + imgPort + "/" + imageRepo.Prefix
					} else {
						initOption.OtherRepo = imageRepo.Domain + imgPort + "/" + imageRepo.Prefix
					}
				}
			} else {
				if len(imageRepo.Domain) > 0 {
					_, err := utils.LoopIP(imageRepo.Domain)
					if err != nil {
						return err
					}
					initOption.OtherRepo = imageRepo.Domain + imgPort + "/" + imageRepo.Prefix
				}
			}

			httpRepo := bkeCluster.Spec.ClusterConfig.Cluster.HTTPRepo
			httpPort := ":80"
			if len(httpRepo.Port) > 0 {
				httpPort = ":" + httpRepo.Port
			}
			if len(httpRepo.Ip) > 0 {
				if httpRepo.Ip != initOption.HostIP {
					initOption.OtherSource = "http://" + httpRepo.Ip + httpPort
				}
			} else {
				if len(httpRepo.Domain) > 0 {
					if net.ParseIP(httpRepo.Domain) == nil {
						initOption.OtherSource = "http://" + httpRepo.Domain + httpPort
					} else {
						_, err := utils.LoopIP(httpRepo.Domain)
						if err != nil {
							return err
						}
						initOption.OtherSource = "http://" + httpRepo.Domain + httpPort
					}
				}
			}
			if len(httpRepo.Port) > 0 {
				initOption.YumRepoPort = httpRepo.Port
			}
			if len(bkeCluster.Spec.ClusterConfig.Cluster.NTPServer) > 0 {
				initOption.NtpServer = bkeCluster.Spec.ClusterConfig.Cluster.NTPServer
			}
		}
		if len(initOption.ClusterAPI) == 0 {
			initOption.ClusterAPI = "latest"
		}
		if len(initOption.OFVersion) == 0 {
			initOption.OFVersion = "latest"
		}
		if len(initOption.VersionUrl) == 0 {
			initOption.VersionUrl = utils.DefaultPatchDownURL
		}
		if len(initOption.NtpServer) == 0 {
			initOption.NtpServer = configinit.DefaultNTPServer
		}
		if len(initOption.Runtime) == 0 {
			initOption.Runtime = "docker"
		}

		if len(initOption.RuntimeStorage) == 0 {
			if initOption.Runtime == "docker" {
				initOption.RuntimeStorage = configinit.DefaultCRIDockerDataRootDir
			} else {
				initOption.RuntimeStorage = configinit.DefaultCRIContainerdDataRootDir
			}
		}

		fmt.Println(fmt.Sprintf("--hostIP:            %s", initOption.HostIP))
		fmt.Println(fmt.Sprintf("--domain:            %s", initOption.Domain))
		fmt.Println(fmt.Sprintf("--kubernetesPort:    %s", initOption.KubernetesPort))
		fmt.Println(fmt.Sprintf("--imageRepoPort:     %s", initOption.ImageRepoPort))
		fmt.Println(fmt.Sprintf("--yumRepoPort:       %s", initOption.YumRepoPort))
		fmt.Println(fmt.Sprintf("--chartRepoPort:     %s", initOption.ChartRepoPort))
		fmt.Println(fmt.Sprintf("--ntpServer:         %s", initOption.NtpServer))
		fmt.Println(fmt.Sprintf("--runtime:           %s", initOption.Runtime))
		fmt.Println(fmt.Sprintf("--runtimeStorage:    %s", initOption.RuntimeStorage))
		fmt.Println(fmt.Sprintf("--clusterAPI:        %s", initOption.ClusterAPI))
		fmt.Println(fmt.Sprintf("--oFVersion:         %s", initOption.OFVersion))
		fmt.Println(fmt.Sprintf("--versionUrl:        %s", initOption.VersionUrl))
		fmt.Println(fmt.Sprintf("--enableNTP:         %v", initOption.EnableNTP))
		//fmt.Println(fmt.Sprintf("--file:              %s", initOption.File))
		if initOption.File != "" {
			fmt.Println(fmt.Sprintf("--file:              %s", initOption.File))
		}
		if initOption.OtherRepo != "" {
			fmt.Println(fmt.Sprintf("--otherRepo:         %s", initOption.OtherRepo))
		}
		if initOption.OtherSource != "" {
			fmt.Println(fmt.Sprintf("--otherSource:       %s", initOption.OtherSource))
		}

		if initOption.HostIP == "" {
			return errors.New("The host IP address must be set. ")
		}
		if initOption.KubernetesPort == "" {
			return errors.New("The kubernetes port must be set. ")
		}
		if initOption.Domain == "" {
			return errors.New("The host domain name is a must. ")
		}
		if initOption.ImageRepoPort == "" {
			return errors.New("The image server port must be set. ")
		}
		if initOption.ChartRepoPort == "" {
			return errors.New("The yum chart port must be set. ")
		}
		if initOption.YumRepoPort == "" {
			return errors.New("The yum server port must be set. ")
		}
		if !utils.IsNum(initOption.ImageRepoPort) {
			return errors.New("The image server port is not a number. ")
		}
		if !utils.IsNum(initOption.YumRepoPort) {
			return errors.New("The yum server port is not a number. ")
		}
		if !utils.IsNum(initOption.ChartRepoPort) {
			return errors.New("The chart server port is not a number. ")
		}
		if len(initOption.OtherSource) > 0 {
			if !strings.HasPrefix(initOption.OtherSource, "http://") {
				return errors.New("The parameter `otherSource` must start with http:// ")
			}
		}
		if !confirm {
			con := ""
			for {
				fmt.Print("Confirm the parameters, press Y to continue N will exit. [Y/N]? ")
				_, _ = fmt.Scan(&con)
				if !utils.ContainsString([]string{"N", "Y"}, strings.ToUpper(con)) {
					continue
				}
				break
			}
			if strings.ToUpper(con) == "N" {
				os.Exit(0)
			}
		}
		return nil
	},
	Run: func(cmd *cobra.Command, args []string) {
		initOption.Args = args
		initOption.Options = options
		initOption.Initialize()
	},
}

func init() {
	rootCmd.AddCommand(initCmd)

	// Here you will define your flags and configuration settings.
	initCmd.Flags().StringVarP(&initOption.File, "file", "f", "", "bkecluster.yaml")
	initCmd.Flags().StringVar(&initOption.Domain, "domain", configinit.DefaultImageRepo, "The domain name of the host is customized")
	initCmd.Flags().StringVar(&initOption.ImageRepoPort, "imageRepoPort", configinit.DefaultImageRepoPort, "image repository port")
	initCmd.Flags().StringVar(&initOption.HostIP, "hostIP", "", "local kubernetes api server")
	initCmd.Flags().StringVar(&initOption.KubernetesPort, "kubernetesPort", utils.DefaultKubernetesPort, "local kubernetes port")
	initCmd.Flags().StringVar(&initOption.YumRepoPort, "yumRepoPort", configinit.DefaultYumRepoPort, "yum repository port")
	initCmd.Flags().StringVar(&initOption.ChartRepoPort, "chartRepoPort", utils.DefaultChartRegistryPort, "chart repository port")
	initCmd.Flags().StringVar(&initOption.NtpServer, "ntpServer", configinit.DefaultNTPServer, "value is `local`, an ntp service is started on the host")
	initCmd.Flags().StringVar(&initOption.Runtime, "runtime", "containerd", "docker/containerd")
	initCmd.Flags().StringVarP(&initOption.RuntimeStorage, "runtimeStorage", "s", "", "default `/var/lib/docker or /var/lib/containerd`")

	// online installation mode
	initCmd.Flags().StringVar(&initOption.OtherRepo, "otherRepo", "", "Mirror repository or special mirror address")
	initCmd.Flags().StringVar(&initOption.OtherSource, "otherSource", "", "Provides the private source address for system package installation")

	initCmd.Flags().StringVar(&initOption.ClusterAPI, "clusterAPI", "latest", "cluster-api-bke version")
	initCmd.Flags().BoolVar(&confirm, "confirm", false, "Skip the confirmation")
	initCmd.Flags().StringVarP(&initOption.OFVersion, "oFVersion", "v", "latest", "openfuyao version")
	initCmd.Flags().StringVar(&initOption.VersionUrl, "versionUrl", utils.DefaultPatchDownURL, "online openfuyao version config download url")

	initCmd.Flags().BoolVar(&initOption.InstallConsole, "installConsole", true, "Whether to install bkeconsole (default: true)")
	initCmd.Flags().BoolVar(&initOption.EnableNTP, "enableNTP", true, "enable or disable NTP service, default is true")

	initCmd.Flags().BoolVar(&initOption.ImageRepoTLSVerify, "imageRepoTLSVerify", true, "Enable TLS verification for image repository")
	initCmd.Flags().StringVar(&initOption.ImageRepoCAFile, "imageRepoCAFile", "", "CA certificate file for image repository")
	initCmd.Flags().StringVar(&initOption.ImageRepoUsername, "imageRepoUsername", "", "Username for image repository authentication")
	initCmd.Flags().StringVar(&initOption.ImageRepoPassword, "imageRepoPassword", "", "Password for image repository authentication")

}
